/**
 * @file soft_I2C.c
 * @author your name (you@domain.com)
 * @brief 软件I2C
 * @version 0.1
 * @date 2025-05-12
 * 
 * @copyright Copyright (c) 2025
 * 
 */

 #include "soft_I2C.h"


/****************************** 1. 配置函数 *****************************************/

//移植时请根据自身单片机修改
 
 
void Soft_I2C_Config(Soft_I2C_Handle* si2c, GPIO_TypeDef* scl_port, uint32_t scl_pin, GPIO_TypeDef* sda_port, uint32_t sda_pin)
{
    si2c->SCL_Port = scl_port;
    si2c->SCL_Pin = scl_pin;
    si2c->SDA_Port = sda_port;
    si2c->SDA_Pin = sda_pin;
}
 
/****************************** 2. 辅助宏/函数定义 *****************************************/

#define I2C_PIN_HIGH GPIO_PIN_SET
#define I2C_PIN_LOW  GPIO_PIN_RESET

//移植时请根据自身单片机修改

static inline void SCL_Low(Soft_I2C_Handle* si2c) {
    HAL_GPIO_WritePin(si2c->SCL_Port, si2c->SCL_Pin, GPIO_PIN_RESET);
}

static inline void SCL_High(Soft_I2C_Handle* si2c) {
    HAL_GPIO_WritePin(si2c->SCL_Port, si2c->SCL_Pin, GPIO_PIN_SET);
}

static inline void SDA_Low(Soft_I2C_Handle* si2c) {
    HAL_GPIO_WritePin(si2c->SDA_Port, si2c->SDA_Pin, GPIO_PIN_RESET);
}

static inline void SDA_High(Soft_I2C_Handle* si2c) {
    HAL_GPIO_WritePin(si2c->SDA_Port, si2c->SDA_Pin, GPIO_PIN_SET);
}

static inline GPIO_PinState SDA_Read(Soft_I2C_Handle* si2c) {
    return HAL_GPIO_ReadPin(si2c->SDA_Port, si2c->SDA_Pin);
}

 
/**
 * @brief 设置SDA引脚为输入
 * 
 * @param si2c 
 */
static void SDA_SetInput(Soft_I2C_Handle* si2c)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = si2c->SDA_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(si2c->SDA_Port, &GPIO_InitStruct);
}

/**
 * @brief 设置SDA引脚为输出
 * 
 * @param si2c 
 */
static void SDA_SetOutput(Soft_I2C_Handle* si2c)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = si2c->SDA_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(si2c->SDA_Port, &GPIO_InitStruct);
}
 
 
 /****************************** 3. I2C 协议辅助函数 *****************************************/
 
 /**
  * @brief 产生IIC起始信号
  * 
  * @param si2c 
  */
static void I2C_Start(Soft_I2C_Handle* si2c) {
    SDA_SetOutput(si2c);  
    SDA_High(si2c);
    SCL_High(si2c);
    softDelayMicro(4);
    SDA_Low(si2c);//START：当CLK高时，DATA由高变低
    softDelayMicro(4);
    SCL_Low(si2c);//钳住I2C总线，准备发送或接收数据
}
 
/**
 * @brief 产生IIC停止信号
 * 
 * @param si2c 
 */
static void I2C_Stop(Soft_I2C_Handle* si2c) {
    SDA_SetOutput(si2c);
    SCL_Low(si2c);
    SDA_Low(si2c);//STOP:当CLK高时，数据由低变高
    softDelayMicro(4);
    SCL_High(si2c);
    SDA_High(si2c);//发送I2C总线结束信号
    softDelayMicro(4);
}
 
static void I2C_SendByte(Soft_I2C_Handle* si2c, uint8_t byte) {
    SDA_SetOutput(si2c);
    for (int i = 0; i < 8; i++) {
        SCL_Low(si2c);
        if (byte & 0x80)
            SDA_High(si2c);
        else
            SDA_Low(si2c);
        softDelayMicro(1);
        SCL_High(si2c);
        softDelayMicro(1);
        byte <<= 1;
    }
}

/**
 * @brief 等待应答信号到来
 * 
 * @param si2c 
 * @return true 接收应答成功
 * @return false 接收应答失败
 * 
 */
static bool I2C_Wait_Ack(Soft_I2C_Handle* si2c) {
    SCL_Low(si2c);
    SDA_SetInput(si2c);
    SCL_High(si2c);
    softDelayMicro(1);
    uint8_t cycleTimes = 0;
    while(SDA_Read(si2c) != I2C_PIN_LOW)// 从机拉低表示 ACK
    {
        if(cycleTimes > 250)
        {
            I2C_Stop(si2c);
            return false;
        }
        cycleTimes++;
        softDelayMicro(1);
    }
    
    SCL_Low(si2c);
    return true;
}


 
static uint8_t I2C_ReceiveByte(Soft_I2C_Handle* si2c) {
    uint8_t byte = 0;
    SDA_SetInput(si2c);
    for (int i = 0; i < 8; i++) {
        SCL_Low(si2c);
        softDelayMicro(1);
        SCL_High(si2c);
        byte <<= 1;
        if (SDA_Read(si2c) == I2C_PIN_HIGH)
            byte |= 0x01;
        softDelayMicro(1);
    }
    return byte;
}

/**
 * @brief 产生ACK应答
 * 
 * @param si2c 
 */
static void I2C_Ack(Soft_I2C_Handle* si2c) {
    SCL_Low(si2c);
    SDA_SetOutput(si2c);
    SDA_Low(si2c);
    softDelayMicro(2);
    SCL_High(si2c);
    softDelayMicro(2);
    SCL_Low(si2c);
}

/**
 * @brief 不产生ACK应答
 * 
 * @param si2c 
 */
static void I2C_Nack(Soft_I2C_Handle* si2c) {
    SCL_Low(si2c);
    SDA_SetOutput(si2c);
    SDA_High(si2c);
    softDelayMicro(2);
    SCL_High(si2c);
    softDelayMicro(2);
    SCL_Low(si2c);
}
 
 
 /****************** 4. 软件I2C读写函数实现 ***************/
 
 
bool Soft_I2C_Master_Transmit(Soft_I2C_Handle* si2c, uint16_t DevAddress, uint8_t* pData, uint16_t Size) {
    I2C_Start(si2c);

    I2C_SendByte(si2c, DevAddress | 0x00);
    if (!I2C_Wait_Ack(si2c)) return false;

    for (uint16_t i = 0; i < Size; i++) {
        I2C_SendByte(si2c, pData[i]);
        if (!I2C_Wait_Ack(si2c)) return false;
    }

    I2C_Stop(si2c);
    return true;
}
 
bool Soft_I2C_Master_Receive(Soft_I2C_Handle* si2c, uint16_t DevAddress, uint8_t* pData, uint16_t Size) {
    I2C_Start(si2c);

    I2C_SendByte(si2c, DevAddress | 0x01);
    if (!I2C_Wait_Ack(si2c)) return false;

    for (uint16_t i = 0; i < Size; i++) {
        pData[i] = I2C_ReceiveByte(si2c);
        if (i < Size - 1)
            I2C_Ack(si2c);
        else
            I2C_Nack(si2c);
    }

    I2C_Stop(si2c);
    return true;
}